﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace AirDrive.Companion
{
    class AutoDiscoveryServer : IDisposable
    {
        public event Action<Drive> Heartbeat;

        const int AutoDiscoveryPort = 37483;

        private UdpClient _udpClient;
        private bool _stopping = false;

        byte[] _magicPackage = new Guid("07300457-1fbe-485d-ac94-0a0dbbe815ba").ToByteArray();

        private void ReportProgress(string status)
        {
            Debug.WriteLine(string.Format("AutoDiscovery: {0}", status));
        }

        public void Start()
        {
            try
            {
                if (_udpClient != null)
                    Stop();
                
                IPEndPoint LocalEP = new IPEndPoint(IPAddress.Any, AutoDiscoveryPort);
                _udpClient = new UdpClient(LocalEP);

                _udpClient.BeginReceive(MessageReceived, null);
                ReportProgress("Listening " + AutoDiscoveryPort + "/UDP");
            }
            catch (Exception ex)
            {
                ReportProgress(string.Format("Failed - {0}", ex.Message));
            }
        }

        private void MessageReceived(IAsyncResult ar)
        {
            try
            {
                IPEndPoint endPoint = null;
                byte[] message = _udpClient.EndReceive(ar, ref endPoint);

                if (!_stopping)
                    _udpClient.BeginReceive(MessageReceived, null);

                if (message.Length > _magicPackage.Length && message.Take(_magicPackage.Length).SequenceEqual(_magicPackage))
                {
                    ReportProgress("Discovery from " + endPoint + "/UDP");

                    byte[] data = message.Skip(_magicPackage.Length).ToArray();
                    string driveName = null;

                    if (data.Length >= 5)
                    {
                        endPoint = new IPEndPoint(BitConverter.ToInt64(data.Take(4).Concat(Enumerable.Repeat<byte>(0, 4)).ToArray(), 0), data[4]);
                        if (data.Length > 5)
                            driveName = Encoding.UTF8.GetString(data.Skip(5).ToArray());

                        ReportProgress(string.Format("Discovered drive '{0}' on ftp://{1}", driveName, endPoint));

                        if (Heartbeat != null)
                            Heartbeat(new Drive(driveName, endPoint));
                    }
                    else
                        ReportProgress("Wrong data format");
                }
            }
            catch(ObjectDisposedException e)
            {
                ReportProgress("Service stopped");
            }
            catch (Exception e)
            {
                ReportProgress(string.Format("Failed receiving message - {0}", e.Message));
            }
        }

        public void Stop()
        {
            _stopping = true;
            if (_udpClient != null)
            {
                _udpClient.Close();
                _udpClient = null;
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            Heartbeat = null;
            Stop();
        }

        #endregion
    }
}
